OpenPNM comes with a small selection of pre-written phases (Air, Water, Mercury). In many cases users will want different options but it is not feasible or productive to include a wide variety of fluids. Consequntly OpenPNM has a mechanism for creating custom phases for this scneario. This requires that the user have correlations for the properties of interest, such as the viscosity as a function of temperature in the form of a polynomial for instance. This is process is described in the following tutuorial:
Import the usual packages and instantiate a small network for demonstration purposes:
In [1]:
import numpy as np
import openpnm as op
In [12]:
pn = op.network.Cubic(shape=[3, 3, 3], spacing=1e-4)
print(pn)
Now that a network is defined, we can create a GenericPhase object associated with it. For this demo we'll make an oil phase, so let's call it oil:
In [13]:
oil = op.phases.GenericPhase(network=pn)
print(oil)
As can be seen in the above printout, this phase has a temperature and pressure set at all locations, but has no other physical properties.
There are 2 ways add physical properties. They can be hard-coded, or added as a 'pore-scale model'.
Start with hard-coding:
In [35]:
oil['pore.molecular_mass'] = 100.0 # g/mol
In [36]:
print(oil['pore.molecular_mass'])
As can be seen, this puts the value of 100.0 g/mol in every pore. Note that you could also assign each pore explicitly with a numpy array. OpenPNM automatically assigns a scalar value to every location as shown above.
In [37]:
oil['pore.molecular_mass'] = np.ones(shape=[pn.Np, ])*120.0
In [38]:
print(oil['pore.molecular_mass'])
You can also specify something like viscosity this way as well, but it's not recommended:
In [39]:
oil['pore.viscosity'] = 1600.0 # cP
The problem with specifying the viscosity as a hard-coded value is that viscosity is a function of temperature (among other things), so if we adjust the temperature on the oil object it will have no effect on the hard-coded viscosity:
In [40]:
oil['pore.temperature'] = 100.0 # C
print(oil['pore.viscosity'])
The correct way to specify something like viscosity is to use pore-scale models. There is a large libary of pre-written models in the openpnm.models submodule. For instance, a polynomial can be used as follows:
In [29]:
mod = op.models.misc.polynomial
oil.add_model(propname='pore.viscosity', model=mod,
a=[1600, 12, -0.05], prop='pore.temperature')
We can now see that our previously written values of viscosity (1600.0) have been overwritten by the values coming from the model:
In [30]:
print(oil['pore.viscosity'])
And moreover, if we change the temperature the model will update the viscosity values:
In [31]:
oil['pore.temperature'] = 40.0 # C
oil.regenerate_models()
print(oil['pore.viscosity'])
Note the call to regenerate_models, which is necessary to actually re-run the model using the new temperature.
When a pore-scale model is added to an object, it is stored under the models attribute, which is a dictionary with names corresponding the property that is being calculated (i.e. 'pore.viscosity'):
In [33]:
print(oil.models)
We can reach into this dictionary and alter the parameters of the model if necessary:
In [34]:
oil.models['pore.viscosity']['a'] = [1200, 10, -0.02]
oil.regenerate_models()
print(oil['pore.viscosity'])
The models submodule has a variety of common functions, stored under models.misc.basic_math or models.misc.common_funtions. There are also some models specific to physical properties under models.phases.